package com.os.service.impl;

import com.os.content.SwapContent;
import com.os.pojo.virtualmemory.MemoryBlock;
import com.os.pojo.virtualmemory.Page;
import com.os.pojo.vo.ActionVo;
import com.os.service.SwapService;
import lombok.Getter;
import lombok.Setter;

import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

/**
 * 先进先出置换算法
 */
public class SwapServiceImpl<T extends MemoryBlock> implements SwapService<T> {

    private final LinkedList<T> linkedList;

    /**
     * clock算法需要使用的索引
     */
    private int clockIndex;

    @Getter
    private int focusNum;

    @Getter
    private int requestNum;

    @Setter
    private List<T> OPTList;

    @Setter
    @Getter
    private String swapName = SwapContent.FIFO;

    private StringBuilder processInfo;

    /**
     * 用来判断是页还是段
     */
    private T blockKind;
    /**
     * 段号还是页号
     */
    private String blockMsg;

    public SwapServiceImpl(StringBuilder processInfo, T blockKind) {
        linkedList = new LinkedList<>();
        OPTList = new LinkedList<>();
        clockIndex = 0;
        focusNum = 0;
        requestNum = 0;
        this.processInfo = processInfo;
        this.blockKind = blockKind;

        if (blockKind instanceof Page) {
            blockMsg = "页号";
        } else {
            blockMsg = "段号";
        }
    }

    private T getAndRemove(int index) {
        T t = linkedList.get(index);
        linkedList.remove(t);
        return t;
    }

    @Override
    public int len() {
        return linkedList.size();
    }


    /**
     * 所有进程都加1
     */
    @Override
    public void setFIFOIncrese(T item) {
        item.setFIFOTime(0);
        for (T t : linkedList) {
            // 全部都加1
            t.setFIFOTime(t.getFIFOTime() + 1);
        }


    }

    /**
     * 使当前item的time为0，其他都加1
     */
    @Override
    public void setLRUTimeIncrese(T item) {
        // 当命中的时候也调用
        item.setLRUTime(0);
        for (T t : linkedList) {
            // 其他的都加1
            if (!t.equals(item)) t.setLRUTime(t.getLRUTime() + 1);
        }
    }


    @Override
    public void setClockBit(T item) {
        item.setCLOCKBit(true);
    }

    /**
     * 添加时处理OPT序列
     */
    private void takeOPTListRemove() {
        if (OPTList != null && !OPTList.isEmpty()) {
            OPTList.remove(0);
        }
    }

    /**
     * 置换之后不会用到或者最晚用到的块
     */
    private int getOPTItem() {
        // 最久的时间
        int lastTime = -1;
//        T lastItem = null;
        int lastIndex = -1;
        // 遍历目前加载的块，取出之后用到时间最久的那个
        for (int i = 0; i < linkedList.size(); i++) {
            T item = linkedList.get(i);
            int time = OPTList.indexOf(item);
            // 直接返回未找到的
            if (time == -1) return i;
            if (time > lastTime) {
                lastIndex = i;
                lastTime = time;
            }
        }
        if (lastIndex == -1) return -1;

        if (!linkedList.get(lastIndex).isUsed()){
            linkedList.remove(lastIndex);
            return getOPTItem();
        }


        return lastIndex;

    }

    /**
     * 添加进入
     */
    @Override
    public void add(T item) {
        //OPT
        takeOPTListRemove();

        // FIFO
        setFIFOIncrese(item);

        // LRU
        setLRUTimeIncrese(item);

        // CLOCK
        setClockBit(item);

        linkedList.add(item);
    }

    /**
     * 查找LRU或FIFO时间未调用的最久的
     */
    private int getTimeMax(boolean isFIFO) {
        int max = -1;
        int max_index = -1;
        for (int i = 0; i < linkedList.size(); i++) {
            if (!isFIFO && linkedList.get(i).getLRUTime() > max) {
                max = linkedList.get(i).getLRUTime();
                max_index = i;
            } else if (isFIFO && linkedList.get(i).getFIFOTime() > max) {
                max = linkedList.get(i).getFIFOTime();
                max_index = i;
            }
        }
        if (max_index == -1) return -1;

        if (linkedList.get(max_index).isUsed())
            return max_index;
        else{
            // 未找到，递归
            linkedList.remove(max_index);
            return getTimeMax(isFIFO);
        }

    }

    /**
     * 取出，notParid为真则块上级不能为parId
     */
    @Override
    public T get(ActionVo actionVo) {
        T item = null;
        showSwapList(actionVo);
        if (swapName.equals(SwapContent.LRU)) {
            // LRU
            item = getAndRemove(getTimeMax(false));
            setLRUTimeIncrese(item);
        } else if (swapName.equals(SwapContent.CLOCK)) {
            // CLOCK
            item = getAndRemove(getByClock());
            setClockBit(item);
        } else if (swapName.equals(SwapContent.OPT)) {
            // OPT
            item = getAndRemove(getOPTItem());
            takeOPTListRemove();
        } else {
            // FIFO
            item = getAndRemove(getTimeMax(true));
            setFIFOIncrese(item);
        }
        processInfo.append(String.format("移除%s：%s\n", blockMsg, item.getId()));
        return item;
    }


    /**
     * 使用clock算法，计算当前要换出的块
     */
    private int getByClock() {
        do {
            // 防止指针大于列表长度
            clockIndex = Math.min(clockIndex, linkedList.size() - 1);
            T item = linkedList.get(clockIndex);
            int tempIndex = clockIndex;
            // 更新指针
            clockIndex = (clockIndex + 1) % linkedList.size();
            // 为1则换0，为0则淘汰
            if (item.getCLOCKBit()) {
                item.setCLOCKBit(false);
            } else {
                if (item.isUsed()) {
                    // 淘汰
                    return tempIndex;
                }else {
                    // 未找到，递归
                    linkedList.remove(item);
                    return getByClock();
                }
            }
        } while (true);

    }

    private void showSwapList(ActionVo actionVo) {

        processInfo.append("---------------------------\n");
        processInfo.append(String.format("%s置换列表,置换算法为：%s\n", blockMsg, swapName));
        StringBuilder ss = new StringBuilder();


        List<T> list = new ArrayList<>(linkedList);
        switch (swapName) {
            case SwapContent.CLOCK:
                for (T t : list) {
                    if (!t.isUsed()) continue;
                    processInfo.append(String.format("%s：%s 访问位:%s\n", blockMsg, t.getId(), t.getCLOCKBit()));
                    ss.append(String.format("%s|%s ", t.getId(), t.getCLOCKBit() ? "T" : "F"));
                }
                processInfo.append(String.format("当前指针：%s\n", clockIndex));
                break;
            case SwapContent.LRU:
                list.sort((o1, o2) -> o2.getLRUTime() - o1.getLRUTime());
                for (T t : list) {
                    if (!t.isUsed()) continue;
                    processInfo.append(String.format("%s：%s 未使用时间:%s\n", blockMsg, t.getId(), t.getLRUTime()));
                    ss.append(String.format("%s  ", t.getId()));
                }
                break;
//            case SwapContent.OPT:
//                list = OPTList;
//                System.out.println(String.format("%s：%s", blockMsg, list.size()));
//                for (T t : list) {
//                    System.out.print(String.format("%s ", t.getId()));
//                }
//                System.out.println();
//                break;
            default:
                // FIFO
                list.sort((o1, o2) -> o2.getFIFOTime() - o1.getFIFOTime());
                for (T t : list) {
                    if (!t.isUsed()) continue;
                    processInfo.append(String.format("%s：%s 存在时间:%s\n", blockMsg, t.getId(), t.getFIFOTime()));
                    ss.append(String.format("%s  ", t.getId()));
                }
                break;

        }
//        actionVo.setQueueInfo(ss.toString());
        processInfo.append("---------------------------\n");
    }

    @Override
    public void onFocus(T item, ActionVo actionVo) {

        if (blockKind instanceof Page) {
            actionVo.setHasPageFocus(true);
        } else {
            actionVo.setHasSegmentFocus(true);
        }

        showSwapList(actionVo);
        processInfo.append(String.format("%s：%s 命中\n", blockMsg, item.getId()));

        focusNum++;
        takeOPTListRemove();
        setLRUTimeIncrese(item);
        setClockBit(item);
    }

    @Override
    public void addToOPTList(T item) {
        OPTList.add(item);
    }

    @Override
    public void increaseRequestNum() {
        requestNum++;
    }


    @Override
    public void clearLinkedList() {
        linkedList.clear();
        OPTList.clear();
    }
}
